import json
import logging
from decimal import Decimal

from django.contrib.auth.mixins import LoginRequiredMixin
from django.db import transaction
from django.http import HttpResponseForbidden, JsonResponse
from django.shortcuts import render
from django.utils import timezone
from django.views import View
from django_redis import get_redis_connection

from apps.goods.models import SKU
from apps.orders.models import OrderInfo, OrderGoods
from apps.users.models import Address
from utils.mixins import LoginRequiredJSONMixin
from utils.response_code import RETCODE

logger = logging.getLogger('django')

class OrderSettlementView(LoginRequiredMixin, View):
    """结算订单"""

    def get(self, request):
        # 获取当前登录用户对象
        user = request.user
        # 查询当前登录用户是否有收货地址
        try:
            addresses = Address.objects.filter(user=user, is_deleted=False)
            # 如果没有收货地址，那么就跳转到编辑收货地址页面
            if len(addresses) == 0:
                address_list = []
                context = {
                    'addresses': address_list
                }
                return render(request, 'user_center_site.html', context)
        except Exception as e:
            logger.error(e)
        # 从redis数据库中获取购物车数据  {b'1':b'1',b'2:b'2'}
        redis_conn = get_redis_connection('carts')
        redis_cart = redis_conn.hgetall('carts_%s' % user.id)
        # 从redis数据库中获取被勾选的商品sku_id  [b'1']
        redis_selected = redis_conn.smembers('selected_%s' % user.id)
        # 将被勾选的商品sku_id和数量组成一个新的数据
        new_cart_dict = {}
        for sku_id in redis_selected:
            new_cart_dict[int(sku_id)] = int(redis_cart[sku_id])
        # 通过sku模型类查询出在结算订单页面需要展示的商品sku
        sku_ids = new_cart_dict.keys()
        skus = SKU.objects.filter(id__in=sku_ids)

        total_count = 0
        total_amount = Decimal(0.00)
        for sku in skus:
            # 遍历skus为每个sku补充count（数量）和amount（小计）
            sku.count = new_cart_dict[sku.id]
            sku.amount = sku.price * sku.count
            # # 累加数量和金额
            total_count += sku.count
            total_amount += sku.amount
        # 定义运费10元
        freight = Decimal(10.00)
        context = {
            'addresses': addresses,
            'skus': skus,
            'total_count': total_count,
            'total_amount': total_amount,
            'freight': freight,
            'payment_amount': total_amount + freight
        }
        return render(request, 'place_order.html', context=context)

class OrderCommitView(LoginRequiredJSONMixin, View):
    def post(self, request):
        # 接收参数
        json_dict = json.loads(request.body.decode())
        address_id = json_dict.get('address_id')
        pay_method = json_dict.get('pay_method')
        # 校验参数
        if not all([address_id, pay_method]):
            return HttpResponseForbidden('缺少必传参数')
        try:
            address = Address.objects.get(id=address_id)
        except Address.DoesNotExist:
            return HttpResponseForbidden('参数address_id有误')
        if pay_method not in [OrderInfo.PAY_METHODS_ENUM['CASH'], OrderInfo.PAY_METHODS_ENUM['ALIPAY']]:
            return HttpResponseForbidden('参数pay_method有误')
        with transaction.atomic():
            try:
                # 设置保存点
                save_id = transaction.savepoint()
                # 获取登录用户
                user = request.user
                ordr_id = timezone.localtime().strftime('%Y%m%d%H%M%S') + ('%09d' % user.id)
                # 保存订单基本信息
                order = OrderInfo.objects.create(
                    order_id=ordr_id,
                    user=user,
                    address=address,
                    total_count=0,
                    total_amount=Decimal(0.00),
                    freight=Decimal(10.00),
                    pay_method=pay_method,
                    status=OrderInfo.ORDER_STATUS_ENUM['UNPAID'] if pay_method == OrderInfo.PAY_METHODS_ENUM[
                        'ALIPAY'] else
                    OrderInfo.ORDER_STATUS_ENUM['UNSEND']
                )
                # 保存订单商品信息
                # 从redis数据库中获取购物车数据  {b'1':b'1',b'2:b'2'}
                redis_conn = get_redis_connection('carts')
                redis_cart = redis_conn.hgetall('carts_%s' % user.id)
                # 从redis数据库中获取被勾选的商品sku_id  [b'1']
                selected = redis_conn.smembers('selected_%s' % user.id)
                # 将被勾选的商品sku_id和数量组成一个新的数据
                carts = {}
                for sku_id in selected:
                    carts[int(sku_id)] = int(redis_cart[sku_id])
                # 通过sku模型类查询出在结算订单页面需要展示的商品sku
                sku_ids = carts.keys()
                # 遍历购物车中被勾选的商品信息
                for sku_id in sku_ids:
                    # 每个商品都有多次的下单的情况，直至库存不满足
                    while True:
                        # 查询sku信息
                        sku = SKU.objects.get(id=sku_id)
                        # 获取商品的原始库存和销量
                        origin_stock = sku.stock
                        origin_sales = sku.sales
                        # 判断用户购买的商品数量是否超出了商品库存
                        sku_count = carts[sku.id]
                        if sku_count > origin_stock:
                            # 回滚到保存点
                            transaction.savepoint_rollback(save_id)
                            return JsonResponse({'code': RETCODE.STOCKERR, 'errmsg': '库存不足'})
                        # 更新sku表中商品库存和商品销量
                        new_stock = origin_stock - sku_count
                        new_sales = origin_sales + sku_count
                        # 使用乐观锁对数据进行更新
                        result = SKU.objects.filter(id=sku_id, stock=origin_stock).update(stock=new_stock,
                                                                                          sales=new_sales)
                        # 如果更新数据时，原始数据发生了变化，返回了0，那么就表明有资源抢夺
                        if result == 0:
                            continue
                        # 更新spu表中商品销量
                        sku.spu.sales += sku_count
                        sku.spu.save()
                        # 保存订单信息
                        OrderGoods.objects.create(
                            order=order,
                            sku=sku,
                            count=sku_count,
                            price=sku.price
                        )
                        # 计算商品的总数量
                        order.total_count += sku_count
                        # 计算商品的总金额
                        order.total_amount += (sku_count * sku.price)
                        # 下单成功，跳出循环
                        break
                # 计算商品的实付款
                order.total_amount += order.freight
                order.save()
            except Exception as e:
                logger.error(e)
                transaction.savepoint_rollback(save_id)
                return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '下单失败'})
            # 提交事务
            transaction.savepoint_commit(save_id)
        # 清除购物车中已经结算的商品
        pl = redis_conn.pipeline()
        pl.hdel('carts_%s' % user.id, *selected)
        pl.srem('selected_%s' % user.id, *selected)
        pl.execute()
        # 返回响应信息
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '下单成功', 'order_id': order.order_id})

class OrderSuccessView(LoginRequiredMixin, View):
    """提交订单成功页面"""

    def get(self, request):
        """提供提交订单成功页面"""
        order_id = request.GET.get('order_id')
        payment_amount = request.GET.get('payment_amount')
        pay_method = request.GET.get('pay_method')
        context = {
            'order_id': order_id,
            'payment_amount': payment_amount,
            'pay_method': pay_method
        }
        return render(request, 'order_success.html', context)